package com.sc.nft.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Maps;
import com.sc.nft.dao.*;
import com.sc.nft.entity.*;
import com.sc.nft.entity.dto.*;
import com.sc.nft.entity.vo.*;
import com.sc.nft.entity.vo.tradingPool.*;
import com.sc.nft.enums.*;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.service.TradingPoolConfigService;
import com.sc.nft.service.UserInfoService;
import com.sc.nft.service.UserWalletService;
import com.sc.nft.sup.ErrorCode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author PC
 * @description 针对表【sc_trading_pool_config(生态池配置)】的数据库操作Service实现
 * @createDate 2024-10-26 13:55:44
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class TradingPoolConfigServiceImpl implements TradingPoolConfigService {

    private final TradingPoolConfigDao tradingPoolConfigDao;
    private final TradingPoolAccountLogDao tradingPoolAccountLogDao;
    private final DigitalCollectionDao digitalCollectionDao;
    private final TradingPoolAccountDao tradingPoolAccountDao;
    private final UserCreatorInfoDao userCreatorInfoDao;
    private final FilterIncomeListDao filterIncomeListDao;
    private final TradingDestructionConfigDao tradingDestructionConfigDao;
    private final UserInfoDao userInfoDao;
    private final TradingPoolOrderDao tradingPoolOrderDao;
    private final ShareProfitPanelDao shareProfitPanelDao;
    private final TradingPoolVirtualAccountDao tradingPoolVirtualAccountDao;
    private final UserInfoService userInfoService;
    private final Redisson redisson;
    private final TradingPoolChangeLogDao tradingPoolChangeLogDao;
    private final UserCollectionDao userCollectionDao;
    private final UserWalletService userWalletService;
    private final CatenaInfoDao catenaInfoDao;
    private final RedisTemplate redisTemplate;
    private final CollectionChainInitDetailsLogDao collectionChainInitDetailsLogDao;
    private final UserCollectionChainLogDao userCollectionChainLogDao;
    private final UserChainInfoDao userChainInfoDao;
    private final TradingPoolDestroyRecordDao tradingPoolDestroyRecordDao;


    /**
     * 获取简单的交易池配置
     *
     * @param id daoId
     * @return
     */
    @Override
    public TradingPoolEasyConfigVO getTradingPoolEasyConfig(Long id) {
        TradingPoolConfig tradingPoolEasyConfig = tradingPoolConfigDao.getTradingPoolEasyConfig(id);
        if (BeanUtil.isEmpty(tradingPoolEasyConfig)) {
            return new TradingPoolEasyConfigVO(null);
        }
        return new TradingPoolEasyConfigVO(tradingPoolEasyConfig);
    }

    /**
     * 获取交易池详情
     *
     * @param id
     * @return
     */
    @Override
    public TradingPoolDetailsVO getTradingPoolDetails(Long id) {
        // 获取配置信息
        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(id);
        if (BeanUtil.isEmpty(tradingPoolConfig)) {
            throw new GlobalRunTimeException("当前交易池已关闭");
        }
        // 获取藏品信息
        DigitalCollection digitalCollection = digitalCollectionDao.getById(tradingPoolConfig.getCollectionId());
        if (BeanUtil.isEmpty(digitalCollection)) {
            throw new GlobalRunTimeException("当前交易池交易藏品已下架");
        }
        //作者
        UserCreatorInfo userCreatorInfo = userCreatorInfoDao.getById(digitalCollection.getUserCreatorId());
        if (BeanUtil.isEmpty(userCreatorInfo)) {
            throw new GlobalRunTimeException("当前交易池发行方已下架");
        }

        TradingPoolDetailsVO.Wrapper wrapper = new TradingPoolDetailsVO.Wrapper();
        wrapper.setId(tradingPoolConfig.getId());
        wrapper.setCreationContent(digitalCollection.getCreationContent());
        wrapper.setFirstTitle(digitalCollection.getFirstTitle());
        // 系列
        wrapper.setCatenaName(catenaInfoDao.getCatenaNameById(digitalCollection.getCatenaId()));
        wrapper.setName(tradingPoolConfig.getName());
        wrapper.setSumCount(digitalCollection.getSumCount());
        wrapper.setCurrentPrice(tradingPoolConfig.getCurrentPrice());
        wrapper.setCurrentStock(tradingPoolConfig.getCurrentStock());
        wrapper.setDealToday(tradingPoolConfig.getTodayVolume());
        wrapper.setTodaySHighest(tradingPoolConfig.getTodayHighest());
        wrapper.setTodayLowest(tradingPoolConfig.getTodayLowest());
        wrapper.setIssuerName(userCreatorInfo.getDaoName());
        wrapper.setIssuerAvatar(userCreatorInfo.getCreatorAvatarImg());
        wrapper.setRule(tradingPoolConfig.getActivityRule());
        wrapper.setWorkStory(digitalCollection.getProductPresentation());
        wrapper.setProductDetailPictureType(digitalCollection.getProductDetailPictureType());
        wrapper.setStartTime(DateTime.of(tradingPoolConfig.getStartTime()));
        wrapper.setEndTime(DateTime.of(tradingPoolConfig.getEndTime()));
        wrapper.setSkipUrl(digitalCollection.getSkipUrl());
        wrapper.setProductDetailPicture(digitalCollection.getProductDetailPicture());
        wrapper.setModelImg(digitalCollection.getThreeModelId());
        wrapper.setModelDistance(digitalCollection.getModelDistance());
        return new TradingPoolDetailsVO(wrapper, tradingPoolConfig);
    }

    @Override
    public TradingPoolCalculatePriceRatioRangeVO calculatePriceRatioRange(TradingPoolCalculatePriceRatioRangeDTO tradingPoolCalculatePriceRatioRangeDTO) {
        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(tradingPoolCalculatePriceRatioRangeDTO.getId());
        if (BeanUtil.isEmpty(tradingPoolConfig)) {
            throw new GlobalRunTimeException("交易池已关闭或不存在");
        }
        // 当前价格
        BigDecimal currentPrice = tradingPoolConfig.getCurrentPrice();
        TradingPoolCalculatePriceRatioRangeVO tradingPoolCalculatePriceRatioRangeVO = new TradingPoolCalculatePriceRatioRangeVO();
        // 根据买卖来进行手续费扣除
        BigDecimal maxDec = currentPrice.multiply((BigDecimal.ONE.subtract(tradingPoolCalculatePriceRatioRangeDTO.getMaxDecreasePercentage())));
        BigDecimal maxInc = currentPrice.multiply((BigDecimal.ONE.add(tradingPoolCalculatePriceRatioRangeDTO.getMaxIncreasePercentage())));
        if (tradingPoolCalculatePriceRatioRangeDTO.getTransactionType() == TradingPoolTransactionTypeEnum.BUY) {
            // 买
            tradingPoolCalculatePriceRatioRangeVO.setMinPrice(maxDec);
            tradingPoolCalculatePriceRatioRangeVO.setMaxPrice(maxInc);
            tradingPoolCalculatePriceRatioRangeVO.setPrice(currentPrice);
        } else {
            // 卖 需要在原有的基础上扣除手续费和gas
            tradingPoolCalculatePriceRatioRangeVO.setMinPrice(maxDec.subtract(currentPrice.multiply(tradingPoolConfig.getFeeRate())).subtract(tradingPoolConfig.getGas()).setScale(2, RoundingMode.DOWN));
            tradingPoolCalculatePriceRatioRangeVO.setMaxPrice(maxInc.subtract(currentPrice.multiply(tradingPoolConfig.getFeeRate())).subtract(tradingPoolConfig.getGas()).setScale(2, RoundingMode.DOWN));
            tradingPoolCalculatePriceRatioRangeVO.setPrice(currentPrice);
        }
        if (NumberUtil.isLess(Convert.toBigDecimal(tradingPoolCalculatePriceRatioRangeVO.getMinPrice()), BigDecimal.ZERO) || NumberUtil.isLess(Convert.toBigDecimal(tradingPoolCalculatePriceRatioRangeVO.getMaxPrice()), BigDecimal.ZERO)) {
            throw new GlobalRunTimeException("当前比例会导致您的收益为0");
        }
        return tradingPoolCalculatePriceRatioRangeVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean createConf(TradingPoolCreateConfRpcDTO request) {
        String key = RedisKeyEnum.TRADING_POOL.getKey() + request.getDaoId();
        RLock lock = redisson.getLock(key);
        try {
            //尝试数据加锁
            if (!lock.tryLock(30, TimeUnit.SECONDS)) {
                throw new GlobalRunTimeException("操作频繁，请稍后重试");
            }

            //校验创建交易池请求参数
            validateCreateConf(request);
            //region 品牌
            UserCreatorInfo userCreatorInfo = userCreatorInfoDao.getById(request.getDaoId());
            Assert.notNull(userCreatorInfo, () -> new GlobalRunTimeException("品牌不存在"));
            //endregion
            //region 扣减礼包收入账户金额
            List<TradingPoolAccount> tradingPoolAccountList = tradingPoolAccountDao.listByDaoId(request.getDaoId());
            Assert.isTrue(CollectionUtil.isNotEmpty(tradingPoolAccountList), () -> new GlobalRunTimeException("礼包收入账户金额不足"));
            Assert.isTrue(tradingPoolAccountList.size() == 1, () -> new GlobalRunTimeException("礼包收入账户异常"));
            TradingPoolAccount tradingPoolAccount = tradingPoolAccountList.get(0);
            Assert.isTrue(tradingPoolAccount.getGiftIncomeAccount().compareTo(request.getCurrentAmount()) >= 0, () -> new GlobalRunTimeException("礼包收入账户金额不足"));
            Assert.isTrue(tradingPoolAccountDao.reduceGiftIncomeAccount(tradingPoolAccount.getId(), request.getCurrentAmount()), () -> new GlobalRunTimeException("礼包收入账户金额不足"));
            //endregion
            //region 扣减藏品可用库存
            DigitalCollection collection = digitalCollectionDao.getById(request.getCollectionId());
            Assert.notNull(collection, () -> new GlobalRunTimeException("未找到藏品"));
            Assert.isTrue(Objects.equals(collection.getUserCreatorId(), request.getDaoId()), () -> new GlobalRunTimeException("藏品不属于本品牌"));
            Assert.isTrue(Objects.equals(collection.getCirculationType(), CirculationTypeEnum.TRANS_POOL), () -> new GlobalRunTimeException("藏品的流通类型必须为交易池"));
            Assert.isTrue(collection.getUsableCount() - request.getCurrentStock() >= 0, () -> new GlobalRunTimeException("藏品可用库存不足"));
            Assert.isTrue(digitalCollectionDao.updateUsableStock(collection.getId(), Boolean.FALSE, request.getCurrentStock()) > 0, () -> new GlobalRunTimeException("藏品可用库存不足"));
            //endregion
            //region 新建交易池配置
            TradingPoolConfig tradingPoolConfig = new TradingPoolConfig();
            tradingPoolConfig.setDaoId(request.getDaoId());
            tradingPoolConfig.setName(request.getName().trim());
            tradingPoolConfig.setStartTime(request.getStartTime());
            tradingPoolConfig.setEndTime(request.getEndTime());
            tradingPoolConfig.setStatus(Boolean.FALSE);
            tradingPoolConfig.setCollectionId(request.getCollectionId());
            tradingPoolConfig.setCurrentAmount(request.getCurrentAmount());
            tradingPoolConfig.setCurrentStock(request.getCurrentStock());
            tradingPoolConfig.setCurrentPrice(request.getCurrentAmount().divide(BigDecimal.valueOf(request.getCurrentStock()), 2, RoundingMode.HALF_UP));
            tradingPoolConfig.setTodayVolume(0);
            tradingPoolConfig.setTodayHighest(BigDecimal.ZERO);
            tradingPoolConfig.setTodayLowest(BigDecimal.ZERO);
            tradingPoolConfig.setMaxIncreasePercentage(request.getMaxIncreasePercentage().divide(new BigDecimal(100), 4, RoundingMode.HALF_UP));
            tradingPoolConfig.setMaxDecreasePercentage(request.getMaxDecreasePercentage().divide(new BigDecimal(100), 4, RoundingMode.HALF_UP));
            tradingPoolConfig.setMaxPrice(request.getMaxPrice());
            tradingPoolConfig.setMinPrice(request.getMinPrice());
            tradingPoolConfig.setDestructionPercentage(null);
            tradingPoolConfig.setFeeRate(request.getFeeRate().divide(new BigDecimal(100), 4, RoundingMode.HALF_UP));
            tradingPoolConfig.setGas(request.getGas());
            tradingPoolConfig.setActivityRule(request.getActivityRule());
            if (!tradingPoolConfigDao.save(tradingPoolConfig)) {
                throw new GlobalRunTimeException(ErrorCode.OPERATE_FAIL);
            }
            //endregion
            //region 交易池记录
            TradingPoolChangeLog tradingPoolChangeLog = new TradingPoolChangeLog();
            tradingPoolChangeLog.setTradingPoolConfigId(tradingPoolConfig.getId());
            tradingPoolChangeLog.setRecordTime(DateTime.now());
            tradingPoolChangeLog.setDaoId(request.getDaoId());
            tradingPoolChangeLog.setOperationContent(TradingPoolChangeOperationContentEnum.AMOUNT);
            tradingPoolChangeLog.setQuantity(request.getCurrentAmount());
            tradingPoolChangeLog.setBeforeChange(BigDecimal.ZERO);
            tradingPoolChangeLog.setAfterChange(request.getCurrentAmount());
            tradingPoolChangeLog.setType(TradingPoolChangeTypeEnum.ADD);
            tradingPoolChangeLog.setRemark(TradingPoolChangeTypeEnum.ADD.getDescription() + TradingPoolChangeOperationContentEnum.AMOUNT.getDescription());
            if (!tradingPoolChangeLogDao.save(tradingPoolChangeLog)) {
                throw new GlobalRunTimeException(ErrorCode.OPERATE_FAIL);
            }

            tradingPoolChangeLog = new TradingPoolChangeLog();
            tradingPoolChangeLog.setTradingPoolConfigId(tradingPoolConfig.getId());
            tradingPoolChangeLog.setRecordTime(DateTime.now());
            tradingPoolChangeLog.setDaoId(request.getDaoId());
            tradingPoolChangeLog.setOperationContent(TradingPoolChangeOperationContentEnum.NFT);
            tradingPoolChangeLog.setQuantity(new BigDecimal(request.getCurrentStock()));
            tradingPoolChangeLog.setBeforeChange(BigDecimal.ZERO);
            tradingPoolChangeLog.setAfterChange(new BigDecimal(request.getCurrentStock()));
            tradingPoolChangeLog.setType(TradingPoolChangeTypeEnum.ADD);
            tradingPoolChangeLog.setRemark(TradingPoolChangeTypeEnum.ADD.getDescription() + TradingPoolChangeOperationContentEnum.NFT.getDescription());
            if (!tradingPoolChangeLogDao.save(tradingPoolChangeLog)) {
                throw new GlobalRunTimeException(ErrorCode.OPERATE_FAIL);
            }
            //endregion
            //region 账户变化
            TradingPoolAccountLog tradingPoolAccountLog = new TradingPoolAccountLog();
            tradingPoolAccountLog.setDaoId(request.getDaoId());
            tradingPoolAccountLog.setTradingPoolConfigId(tradingPoolConfig.getId());
            tradingPoolAccountLog.setOperationTime(DateTime.now());
            tradingPoolAccountLog.setQuantity(request.getCurrentAmount());
            tradingPoolAccountLog.setBeforeChange(tradingPoolAccount.getGiftIncomeAccount());
            tradingPoolAccountLog.setAfterChange(tradingPoolAccount.getGiftIncomeAccount().subtract(request.getCurrentAmount()));
            tradingPoolAccountLog.setCategory(TradingPoolAccountCategoryEnum.GIFT_INCOME_ACCOUNT);
            tradingPoolAccountLog.setIsAdd(Boolean.FALSE);
            tradingPoolAccountLog.setSource(TradingPoolAccountTypeEnum.GIFT_INCOME_ACCOUNT);
            tradingPoolAccountLog.setBeneficiary(TradingPoolAccountTypeEnum.POOL_ACCOUNT);
            if (!tradingPoolAccountLogDao.save(tradingPoolAccountLog)) {
                throw new GlobalRunTimeException(ErrorCode.OPERATE_FAIL);
            }
            //endregion

            TradingPoolVirtualAccount tradingPoolVirtualAccount = tradingPoolVirtualAccountDao.getByDaoId(request.getDaoId());
            if (Objects.isNull(tradingPoolVirtualAccount)) {
                UserInfo userInfo = userInfoDao.getById(userCreatorInfo.getUserId());
                Assert.notNull(userInfo, () -> new GlobalRunTimeException("账号不存在"));
                //注册账号
                String userAccount = null;
                UserInfo existUserInfo = null;
                int i = 0;
                do {
                    userAccount = userInfo.getUserTel() + (int) (Math.random() * 100);
                    existUserInfo = userInfoDao.getByUserTel(userAccount);
                    i++;
                }
                while (Objects.nonNull(existUserInfo) && i <= 100);
                Assert.isTrue(Objects.isNull(existUserInfo), () -> new GlobalRunTimeException("账号异常"));
                UserLoginVO userLoginVO = userInfoService.marketValueAccountRegistration("111111", 1, userAccount, "123456");
                tradingPoolVirtualAccount = new TradingPoolVirtualAccount();
                tradingPoolVirtualAccount.setDaoId(request.getDaoId());
                tradingPoolVirtualAccount.setUserId(userLoginVO.getUserId());
                if (!tradingPoolVirtualAccountDao.save(tradingPoolVirtualAccount)) {
                    throw new GlobalRunTimeException(ErrorCode.OPERATE_FAIL);
                }
            }
        } catch (Exception e) {
            log.error("新建交易池异常", e);
            throw new GlobalRunTimeException(e.getMessage());
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Boolean.TRUE;
    }

    @Override
    public Boolean updateConf(TradingPoolUpdateConfDTO request, Boolean isAdmin) {
        validateUpdateConf(request);
        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(request.getId());
        Assert.notNull(tradingPoolConfig, () -> new GlobalRunTimeException("交易池不存在"));
        Assert.isFalse(tradingPoolConfig.getStatus(), () -> new GlobalRunTimeException("交易池启用中，不能修改"));
        if (isAdmin) {
            Assert.notNull(request.getFeeRate(), () -> new GlobalRunTimeException("手续费率不能为空"));
            Assert.notNull(request.getGas(), () -> new GlobalRunTimeException("上链费不能为空"));
            Assert.isTrue(request.getFeeRate().compareTo(BigDecimal.ZERO) >= 0, () -> new GlobalRunTimeException("手续费率不能小于0"));
            Assert.isTrue(request.getFeeRate().compareTo(new BigDecimal(100)) <= 0, () -> new GlobalRunTimeException("手续费率不能大于100%"));
            Assert.isTrue(request.getGas().compareTo(BigDecimal.ZERO) >= 0, () -> new GlobalRunTimeException("上链费不能小于0"));
            tradingPoolConfig.setFeeRate(request.getFeeRate().divide(new BigDecimal(100), 4, RoundingMode.HALF_UP));
            tradingPoolConfig.setGas(request.getGas());
        }
        tradingPoolConfig.setName(request.getName().trim());
        tradingPoolConfig.setStartTime(request.getStartTime());
        tradingPoolConfig.setEndTime(request.getEndTime());
        tradingPoolConfig.setMaxIncreasePercentage(request.getMaxIncreasePercentage().divide(new BigDecimal(100), 4, RoundingMode.HALF_UP));
        tradingPoolConfig.setMaxDecreasePercentage(request.getMaxDecreasePercentage().divide(new BigDecimal(100), 4, RoundingMode.HALF_UP));
        tradingPoolConfig.setMaxPrice(request.getMaxPrice());
        tradingPoolConfig.setMinPrice(request.getMinPrice());
        tradingPoolConfig.setActivityRule(request.getActivityRule());
        if (!tradingPoolConfigDao.updateById(tradingPoolConfig)) {
            throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
        }
        return Boolean.TRUE;
    }

    @Override
    public TradingPoolPurchaseDetailsVO tradingPoolPurchaseDetails(Long id) {
        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(id);
        if (BeanUtil.isEmpty(tradingPoolConfig)) {
            throw new GlobalRunTimeException("当前交易池已关闭");
        }
        TradingPoolPurchaseDetailsVO tradingPoolPurchaseDetailsVO = new TradingPoolPurchaseDetailsVO(tradingPoolConfig);
        tradingPoolPurchaseDetailsVO.setCurrentStock(tradingPoolConfig.getCurrentStock());
        tradingPoolPurchaseDetailsVO.setStartTime(DateTime.of(tradingPoolConfig.getStartTime()));
        tradingPoolPurchaseDetailsVO.setEndTime(DateTime.of(tradingPoolConfig.getEndTime()));
        tradingPoolPurchaseDetailsVO.setCurrentPrice(tradingPoolConfig.getCurrentPrice());
        tradingPoolPurchaseDetailsVO.setMaxDecreasePercentage(tradingPoolConfig.getMaxDecreasePercentage());
        tradingPoolPurchaseDetailsVO.setMaxIncreasePercentage(tradingPoolConfig.getMaxIncreasePercentage());
        return tradingPoolPurchaseDetailsVO;
    }

    @Override
    public TradingPoolVO confDetail(Long id) {
        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(id);
        Assert.notNull(tradingPoolConfig, () -> new GlobalRunTimeException("交易池不存在"));
        TradingPoolVO tradingPoolVo = new TradingPoolVO();
        BeanUtil.copyProperties(tradingPoolConfig, tradingPoolVo);
        tradingPoolVo.setMaxIncreasePercentage(tradingPoolVo.getMaxIncreasePercentage().multiply(new BigDecimal(100)));
        tradingPoolVo.setMaxDecreasePercentage(tradingPoolVo.getMaxDecreasePercentage().multiply(new BigDecimal(100)));
        tradingPoolVo.setFeeRate(tradingPoolVo.getFeeRate().multiply(new BigDecimal(100)));
        GetDaoByIdVO getDaoByIdVO = userCreatorInfoDao.getDaoById(tradingPoolConfig.getDaoId());
        if (Objects.nonNull(getDaoByIdVO)) {
            tradingPoolVo.setDaoName(getDaoByIdVO.getDaoName());
        }
        DigitalCollection digitalCollection = digitalCollectionDao.getById(tradingPoolVo.getCollectionId());
        if (Objects.nonNull(digitalCollection)) {
            tradingPoolVo.setCollectionName(digitalCollection.getFirstTitle());
        }
        return tradingPoolVo;
    }

    @Override
    public IPage<TradingPoolVO> confPage(Page page, Long daoId) {
        Page<TradingPoolConfig> tradingPoolConfigPage = tradingPoolConfigDao.page(page, daoId);
        Map<Long, DigitalCollection> collectionMap = Maps.newHashMap();
        Map<Long, UserCreatorInfo> userCreatorInfoMap = Maps.newHashMap();
        if (CollectionUtil.isNotEmpty(tradingPoolConfigPage.getRecords())) {
            List<Long> collectionIdList = tradingPoolConfigPage.getRecords().stream().map(TradingPoolConfig::getCollectionId).distinct().collect(Collectors.toList());
            List<DigitalCollection> digitalCollectionDaoByIds = digitalCollectionDao.getByIds(collectionIdList);
            collectionMap = CollectionUtil.isNotEmpty(digitalCollectionDaoByIds) ? digitalCollectionDaoByIds.stream().collect(Collectors.toMap(DigitalCollection::getId, Function.identity())) : Maps.newHashMap();
            List<Long> daoIdList = tradingPoolConfigPage.getRecords().stream().map(TradingPoolConfig::getDaoId).distinct().collect(Collectors.toList());
            List<UserCreatorInfo> userCreatorInfoList = userCreatorInfoDao.getDaoByIds(daoIdList);
            userCreatorInfoMap = CollectionUtil.isNotEmpty(userCreatorInfoList) ? userCreatorInfoList.stream().collect(Collectors.toMap(UserCreatorInfo::getId, Function.identity())) : Maps.newHashMap();
        }
        Map<Long, DigitalCollection> finalCollectionMap = collectionMap;
        Map<Long, UserCreatorInfo> finalUserCreatorInfoMap = userCreatorInfoMap;
        return tradingPoolConfigPage.convert(tradingPoolConfig -> {
            TradingPoolVO tradingPoolVo = new TradingPoolVO();
            BeanUtil.copyProperties(tradingPoolConfig, tradingPoolVo);
            DigitalCollection collection = finalCollectionMap.get(tradingPoolConfig.getCollectionId());
            tradingPoolVo.setCollectionName(Objects.nonNull(collection) ? collection.getFirstTitle() : "");
            UserCreatorInfo userCreatorInfo = finalUserCreatorInfoMap.get(tradingPoolConfig.getDaoId());
            tradingPoolVo.setDaoName(Objects.nonNull(userCreatorInfo) ? userCreatorInfo.getDaoName() : "");
            tradingPoolVo.setMaxIncreasePercentage(tradingPoolVo.getMaxIncreasePercentage().multiply(new BigDecimal(100)));
            tradingPoolVo.setMaxDecreasePercentage(tradingPoolVo.getMaxDecreasePercentage().multiply(new BigDecimal(100)));
            tradingPoolVo.setFeeRate(tradingPoolVo.getFeeRate().multiply(new BigDecimal(100)));
            return tradingPoolVo;
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateDestructionConf(TradingPoolDestructionConfDTO request) {
        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(request.getId());
        Assert.notNull(tradingPoolConfig, () -> new GlobalRunTimeException("交易池不存在"));
        Assert.isFalse(tradingPoolConfig.getStatus(), () -> new GlobalRunTimeException("交易池启用中，不能更新销毁配置"));
        Assert.isTrue(request.getDestructionPercentage().compareTo(BigDecimal.ZERO) >= 0, () -> new GlobalRunTimeException("销毁比例不能小于0"));
        Assert.isTrue(request.getDestructionPercentage().compareTo(new BigDecimal(100)) <= 0, () -> new GlobalRunTimeException("销毁比例不能大于100%"));
        Assert.isTrue(CollectionUtil.isNotEmpty(request.getDestructionDetailList()), () -> new GlobalRunTimeException("销毁配置详情不能为空"));
        BigDecimal totalTransferPercentage = BigDecimal.ZERO;
        for (TradingPoolDestructionConfDetailDTO detail : request.getDestructionDetailList()) {
            Assert.notNull(detail.getOperationType(), () -> new GlobalRunTimeException("操作类型不能为空"));
            Assert.notNull(detail.getAccountType(), () -> new GlobalRunTimeException("账号类型不能为空"));
            Assert.notNull(detail.getTransferPercentage(), () -> new GlobalRunTimeException("转入比例不能为空"));
            Assert.isTrue(detail.getTransferPercentage().compareTo(BigDecimal.ZERO) >= 0, () -> new GlobalRunTimeException("转入比例不能小于0"));
            Assert.isTrue(detail.getTransferPercentage().compareTo(new BigDecimal(100)) <= 0, () -> new GlobalRunTimeException("转入比例不能大于100%"));
            if (Objects.equals(detail.getAccountType(), TradingPoolDestructionIAccountTypeEnum.MARKET_ACCOUNT)) {
                Assert.notNull(detail.getUserId(), () -> new GlobalRunTimeException("请选择市值账号"));
                FilterIncomeList filterIncomeList = filterIncomeListDao.getIsTeamIncomeByUserIdAndDaoId(tradingPoolConfig.getDaoId(), detail.getUserId());
                Assert.notNull(filterIncomeList, () -> new GlobalRunTimeException("市值账号不存在"));
            }
            totalTransferPercentage = totalTransferPercentage.add(detail.getTransferPercentage());
        }
        Assert.isTrue(totalTransferPercentage.compareTo(new BigDecimal(100)) == 0, () -> new GlobalRunTimeException("转入比例之和需为100%"));
        List<TradingDestructionConfig> tradingDestructionConfigs = tradingDestructionConfigDao.listByConfId(tradingPoolConfig.getId());
        if (CollectionUtil.isNotEmpty(tradingDestructionConfigs)) {
            for (TradingDestructionConfig destructionConfig : tradingDestructionConfigs) {
                destructionConfig.deleteById();
            }
        }
        tradingPoolConfig.setDestructionPercentage(request.getDestructionPercentage().divide(new BigDecimal(100), 4, RoundingMode.HALF_UP));
        if (!tradingPoolConfigDao.updateById(tradingPoolConfig)) {
            throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
        }
        for (TradingPoolDestructionConfDetailDTO detail : request.getDestructionDetailList()) {
            TradingDestructionConfig tradingDestructionConfig = new TradingDestructionConfig();
            tradingDestructionConfig.setTradingPoolConfigId(tradingPoolConfig.getId());
            tradingDestructionConfig.setOperationType(detail.getOperationType());
            tradingDestructionConfig.setAccountType(detail.getAccountType());
            tradingDestructionConfig.setTransferPercentage(detail.getTransferPercentage().divide(new BigDecimal(100), 4, RoundingMode.HALF_UP));
            if (Objects.equals(detail.getAccountType(), TradingPoolDestructionIAccountTypeEnum.MARKET_ACCOUNT)) {
                tradingDestructionConfig.setUserId(detail.getUserId());
            }
            if (!tradingDestructionConfigDao.save(tradingDestructionConfig)) {
                throw new GlobalRunTimeException(ErrorCode.OPERATE_FAIL);
            }
        }
        return Boolean.TRUE;
    }

    @Override
    public TradingPoolDestructionConfVO destructionConfDetail(Long id) {
        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(id);
        Assert.notNull(tradingPoolConfig, () -> new GlobalRunTimeException("交易池不存在"));
        TradingPoolDestructionConfVO tradingPoolDestructionConfVO = new TradingPoolDestructionConfVO();
        if (Objects.nonNull(tradingPoolConfig.getDestructionPercentage())) {
            tradingPoolDestructionConfVO.setDestructionPercentage(tradingPoolConfig.getDestructionPercentage().multiply(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP));
        }
        List<TradingDestructionConfig> tradingDestructionConfigs = tradingDestructionConfigDao.listByConfId(id);
        List<TradingPoolDestructionConfDetailVO> tradingPoolDestructionConfDetailVOList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(tradingDestructionConfigs)) {
            List<Long> userIdList = tradingDestructionConfigs.stream().map(TradingDestructionConfig::getUserId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
            List<UserInfo> userInfoList = CollectionUtil.isNotEmpty(userIdList) ? userInfoDao.getByIdList(userIdList) : Lists.newArrayList();
            Map<Long, UserInfo> userInfoMap = CollectionUtil.isNotEmpty(userInfoList) ? userInfoList.stream().collect(Collectors.toMap(UserInfo::getId, Function.identity())) : Maps.newHashMap();
            for (TradingDestructionConfig config : tradingDestructionConfigs) {
                TradingPoolDestructionConfDetailVO detailVO = new TradingPoolDestructionConfDetailVO();
                detailVO.setOperationType(config.getOperationType());
                detailVO.setOperationTypeName(config.getOperationType().getDescription());
                detailVO.setAccountType(config.getAccountType());
                detailVO.setAccountTypeName(config.getAccountType().getDescription());
                detailVO.setTransferPercentage(config.getTransferPercentage().multiply(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP));
                if (Objects.nonNull(config.getUserId())) {
                    detailVO.setUserId(config.getUserId());
                    UserInfo userInfo = userInfoMap.get(config.getUserId());
                    detailVO.setUserTel(Objects.nonNull(userInfo) ? userInfo.getUserTel() : "");
                }
                tradingPoolDestructionConfDetailVOList.add(detailVO);
            }
        }
        tradingPoolDestructionConfVO.setDestructionDetailList(tradingPoolDestructionConfDetailVOList);
        return tradingPoolDestructionConfVO;
    }

    @Override
    public Boolean updateConfStatus(Long id, Boolean status) {
        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(id);
        Assert.notNull(tradingPoolConfig, () -> new GlobalRunTimeException("交易池不存在"));
        String key = RedisKeyEnum.TRADING_POOL.getKey() + tradingPoolConfig.getDaoId();
        RLock lock = redisson.getLock(key);
        try {
            //尝试数据加锁
            if (!lock.tryLock(30, TimeUnit.SECONDS)) {
                throw new GlobalRunTimeException("操作频繁，请稍后重试");
            }
            tradingPoolConfig = tradingPoolConfigDao.getById(id);
            Assert.notNull(tradingPoolConfig, () -> new GlobalRunTimeException("交易池不存在"));
            Assert.isFalse(tradingPoolConfig.getStatus() && status, () -> new GlobalRunTimeException("交易池启用中，不能重复启用"));
            Assert.isFalse(!tradingPoolConfig.getStatus() && !status, () -> new GlobalRunTimeException("交易池已关闭，不能重复关闭"));
            if (status) {
                Assert.isTrue(CollectionUtil.isEmpty(tradingPoolConfigDao.listByDaoAndStatus(tradingPoolConfig.getDaoId(), Boolean.TRUE)), () -> new GlobalRunTimeException("当前已有交易池开启"));
                Assert.notNull(tradingPoolConfig.getDestructionPercentage(), () -> new GlobalRunTimeException("请先配置交易池"));
                Assert.isTrue(CollectionUtil.isNotEmpty(tradingDestructionConfigDao.listByConfId(id)), () -> new GlobalRunTimeException("请先配置交易池"));
            }
            tradingPoolConfig.setStatus(status);
            if (!tradingPoolConfigDao.updateById(tradingPoolConfig)) {
                throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
            }
        } catch (Exception e) {
            log.error("", e);
            throw new GlobalRunTimeException(e.getMessage());
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteConf(Long id) {
        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(id);
        Assert.notNull(tradingPoolConfig, () -> new GlobalRunTimeException("交易池不存在"));
        Assert.isFalse(tradingPoolConfig.getStatus(), () -> new GlobalRunTimeException("交易池启用中，删除失败"));
        Assert.isTrue(CollectionUtil.isEmpty(tradingPoolOrderDao.listByConfId(id)), () -> new GlobalRunTimeException("交易池已产生交易数据，删除失败"));

        List<TradingPoolAccount> tradingPoolAccountList = tradingPoolAccountDao.listByDaoId(tradingPoolConfig.getDaoId());
        Assert.isTrue(CollectionUtil.isNotEmpty(tradingPoolAccountList), () -> new GlobalRunTimeException("礼包收入账户异常"));
        Assert.isTrue(tradingPoolAccountList.size() == 1, () -> new GlobalRunTimeException("礼包收入账户异常"));
        TradingPoolAccount tradingPoolAccount = tradingPoolAccountList.get(0);
        Assert.isTrue(tradingPoolAccountDao.addGiftIncomeAccount(tradingPoolAccount.getId(), tradingPoolConfig.getCurrentAmount()), () -> new GlobalRunTimeException("添加礼包收入账户金额失败"));

        Assert.isTrue(digitalCollectionDao.updateUsableStock(tradingPoolConfig.getCollectionId(), Boolean.TRUE, tradingPoolConfig.getCurrentStock()) > 0, () -> new GlobalRunTimeException("添加藏品可用库存失败"));
        //region 交易池记录
        TradingPoolChangeLog tradingPoolChangeLog = new TradingPoolChangeLog();
        tradingPoolChangeLog.setTradingPoolConfigId(tradingPoolConfig.getId());
        tradingPoolChangeLog.setRecordTime(DateTime.now());
        tradingPoolChangeLog.setDaoId(tradingPoolConfig.getDaoId());
        tradingPoolChangeLog.setOperationContent(TradingPoolChangeOperationContentEnum.AMOUNT);
        tradingPoolChangeLog.setQuantity(tradingPoolConfig.getCurrentAmount());
        tradingPoolChangeLog.setBeforeChange(tradingPoolConfig.getCurrentAmount());
        tradingPoolChangeLog.setAfterChange(BigDecimal.ZERO);
        tradingPoolChangeLog.setType(TradingPoolChangeTypeEnum.DEDUCTION);
        tradingPoolChangeLog.setRemark(TradingPoolChangeTypeEnum.DEDUCTION.getDescription() + TradingPoolChangeOperationContentEnum.AMOUNT.getDescription());
        if (!tradingPoolChangeLogDao.save(tradingPoolChangeLog)) {
            throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
        }

        tradingPoolChangeLog = new TradingPoolChangeLog();
        tradingPoolChangeLog.setTradingPoolConfigId(tradingPoolConfig.getId());
        tradingPoolChangeLog.setRecordTime(DateTime.now());
        tradingPoolChangeLog.setDaoId(tradingPoolConfig.getDaoId());
        tradingPoolChangeLog.setOperationContent(TradingPoolChangeOperationContentEnum.NFT);
        tradingPoolChangeLog.setQuantity(new BigDecimal(tradingPoolConfig.getCurrentStock()));
        tradingPoolChangeLog.setBeforeChange(new BigDecimal(tradingPoolConfig.getCurrentStock()));
        tradingPoolChangeLog.setAfterChange(BigDecimal.ZERO);
        tradingPoolChangeLog.setType(TradingPoolChangeTypeEnum.DEDUCTION);
        tradingPoolChangeLog.setRemark(TradingPoolChangeTypeEnum.DEDUCTION.getDescription() + TradingPoolChangeOperationContentEnum.NFT.getDescription());
        if (!tradingPoolChangeLogDao.save(tradingPoolChangeLog)) {
            throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
        }
        //endregion
        //region 账户变化
        TradingPoolAccountLog tradingPoolAccountLog = new TradingPoolAccountLog();
        tradingPoolAccountLog.setDaoId(tradingPoolConfig.getDaoId());
        tradingPoolAccountLog.setTradingPoolConfigId(tradingPoolConfig.getId());
        tradingPoolAccountLog.setOperationTime(DateTime.now());
        tradingPoolAccountLog.setQuantity(tradingPoolConfig.getCurrentAmount());
        tradingPoolAccountLog.setBeforeChange(tradingPoolAccount.getGiftIncomeAccount());
        tradingPoolAccountLog.setAfterChange(tradingPoolAccount.getGiftIncomeAccount().add(tradingPoolConfig.getCurrentAmount()));
        tradingPoolAccountLog.setCategory(TradingPoolAccountCategoryEnum.GIFT_INCOME_ACCOUNT);
        tradingPoolAccountLog.setIsAdd(Boolean.TRUE);
        tradingPoolAccountLog.setSource(TradingPoolAccountTypeEnum.POOL_ACCOUNT);
        tradingPoolAccountLog.setBeneficiary(TradingPoolAccountTypeEnum.GIFT_INCOME_ACCOUNT);
        if (!tradingPoolAccountLogDao.save(tradingPoolAccountLog)) {
            throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
        }
        //endregion
        if (!tradingPoolConfigDao.removeById(tradingPoolConfig.getId())) {
            throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
        }
        return Boolean.TRUE;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addGiftIncomeAccount(Long daoId, BigDecimal amount) {
        String key = RedisKeyEnum.TRADING_POOL_ADD.getKey() + daoId;
        RLock lock = redisson.getLock(key);
        try {
            //尝试数据加锁
            if (!lock.tryLock(30, TimeUnit.SECONDS)) {
                throw new GlobalRunTimeException("操作频繁，请稍后重试");
            }


            List<TradingPoolAccount> accounts = tradingPoolAccountDao.listByDaoId(daoId);
            TradingPoolAccount tradingPoolAccount;
            if (CollectionUtils.isNotEmpty(accounts)) {
                Assert.isTrue(accounts.size() == 1, () -> new GlobalRunTimeException("礼包收入账户异常"));
                tradingPoolAccount = accounts.get(0);
                Assert.isTrue(tradingPoolAccountDao.addGiftIncomeAccount(tradingPoolAccount.getId(), amount), () -> new GlobalRunTimeException("添加礼包收入账户金额失败"));
            } else {
                tradingPoolAccount = new TradingPoolAccount();
                tradingPoolAccount.setFrozenAccount(BigDecimal.ZERO);
                tradingPoolAccount.setGiftIncomeAccount(amount);
                tradingPoolAccount.setDaoId(daoId);
                tradingPoolAccountDao.save(tradingPoolAccount);
            }

            //region 账户变化
            TradingPoolAccountLog tradingPoolAccountLog = new TradingPoolAccountLog();
            tradingPoolAccountLog.setDaoId(daoId);
            tradingPoolAccountLog.setOperationTime(DateTime.now());
            tradingPoolAccountLog.setQuantity(amount);
            tradingPoolAccountLog.setBeforeChange(tradingPoolAccount.getGiftIncomeAccount());
            tradingPoolAccountLog.setAfterChange(tradingPoolAccount.getGiftIncomeAccount().add(amount));
            tradingPoolAccountLog.setCategory(TradingPoolAccountCategoryEnum.GIFT_INCOME_ACCOUNT);
            tradingPoolAccountLog.setIsAdd(Boolean.TRUE);
            tradingPoolAccountLog.setSource(TradingPoolAccountTypeEnum.FIRST_SHARE);
            tradingPoolAccountLog.setBeneficiary(TradingPoolAccountTypeEnum.GIFT_INCOME_ACCOUNT);
            boolean save = tradingPoolAccountLogDao.save(tradingPoolAccountLog);
            if (!save) {
                throw new GlobalRunTimeException("礼包账户日志新增异常");
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean confAdjustStock(TradingPoolConfAdjustStockDTO request) {
        Assert.isTrue(request.getStock() > 0, () -> new GlobalRunTimeException("库存必须大于0"));
        RLock lock = redisson.getLock(RedisKeyEnum.TRADING_POOL_STOCK.getKey() + request.getDaoId());
        try {
            //尝试数据加锁
            if (!lock.tryLock(30, TimeUnit.SECONDS)) {
                throw new GlobalRunTimeException("操作频繁，请稍后重试");
            }
            TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(request.getId());
            Assert.notNull(tradingPoolConfig, () -> new GlobalRunTimeException("交易池不存在"));
            Assert.isTrue(Objects.equals(tradingPoolConfig.getDaoId(), request.getDaoId()), () -> new GlobalRunTimeException("交易池与DAO不匹配"));
            if (tradingPoolConfig.getStatus()) {
                DateTime localityTime = DateTime.of(LocalTime.now().truncatedTo(ChronoUnit.SECONDS).toString(), "HH:mm:ss");
                DateTime startTime = DateTime.of(tradingPoolConfig.getStartTime());
                DateTime endTime = DateTime.of(tradingPoolConfig.getEndTime());
                if (localityTime.isAfter(startTime) && localityTime.isBefore(endTime)) {
                    throw new GlobalRunTimeException("需要在非活动时间内操作");
                }
            }
            if (request.getIsAdd()) {
                DigitalCollection collection = digitalCollectionDao.getById(tradingPoolConfig.getCollectionId());
                Assert.isTrue(collection.getUsableCount() - request.getStock() >= 0, () -> new GlobalRunTimeException("藏品可用库存不足"));
                Assert.isTrue(digitalCollectionDao.updateUsableStock(collection.getId(), Boolean.FALSE, request.getStock()) > 0, () -> new GlobalRunTimeException("藏品可用库存不足"));
                Assert.isTrue(tradingPoolConfigDao.addStock(tradingPoolConfig.getId(), request.getStock()), () -> new GlobalRunTimeException("库存补充失败"));
                //region 交易池记录
                TradingPoolChangeLog tradingPoolChangeLog = new TradingPoolChangeLog();
                tradingPoolChangeLog.setTradingPoolConfigId(tradingPoolConfig.getId());
                tradingPoolChangeLog.setRecordTime(DateTime.now());
                tradingPoolChangeLog.setDaoId(tradingPoolConfig.getDaoId());
                tradingPoolChangeLog.setOperationContent(TradingPoolChangeOperationContentEnum.NFT);
                tradingPoolChangeLog.setQuantity(new BigDecimal(request.getStock()));
                tradingPoolChangeLog.setBeforeChange(new BigDecimal(tradingPoolConfig.getCurrentStock()));
                tradingPoolChangeLog.setAfterChange((new BigDecimal(tradingPoolConfig.getCurrentStock())).add(new BigDecimal(request.getStock())));
                tradingPoolChangeLog.setType(TradingPoolChangeTypeEnum.ADD);
                tradingPoolChangeLog.setRemark(TradingPoolChangeTypeEnum.ADD.getDescription() + TradingPoolChangeOperationContentEnum.NFT.getDescription());
                if (!tradingPoolChangeLogDao.save(tradingPoolChangeLog)) {
                    throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
                }
                //endregion
            } else {
                Assert.isTrue(tradingPoolConfig.getCurrentStock() > request.getStock(), () -> new GlobalRunTimeException("库存不足，扣除失败"));
                Assert.isTrue(tradingPoolConfigDao.reduceStock(tradingPoolConfig.getId(), request.getStock()), () -> new GlobalRunTimeException("库存不足，扣除失败"));
                Assert.isTrue(digitalCollectionDao.updateUsableStock(tradingPoolConfig.getCollectionId(), Boolean.TRUE, request.getStock()) > 0, () -> new GlobalRunTimeException("增加藏品可用库存失败"));
                //region 交易池记录
                TradingPoolChangeLog tradingPoolChangeLog = new TradingPoolChangeLog();
                tradingPoolChangeLog.setTradingPoolConfigId(tradingPoolConfig.getId());
                tradingPoolChangeLog.setRecordTime(DateTime.now());
                tradingPoolChangeLog.setDaoId(tradingPoolConfig.getDaoId());
                tradingPoolChangeLog.setOperationContent(TradingPoolChangeOperationContentEnum.NFT);
                tradingPoolChangeLog.setQuantity(new BigDecimal(request.getStock()));
                tradingPoolChangeLog.setBeforeChange(new BigDecimal(tradingPoolConfig.getCurrentStock()));
                tradingPoolChangeLog.setAfterChange((new BigDecimal(tradingPoolConfig.getCurrentStock())).subtract(new BigDecimal(request.getStock())));
                tradingPoolChangeLog.setType(TradingPoolChangeTypeEnum.DEDUCTION);
                tradingPoolChangeLog.setRemark(TradingPoolChangeTypeEnum.DEDUCTION.getDescription() + TradingPoolChangeOperationContentEnum.NFT.getDescription());
                if (!tradingPoolChangeLogDao.save(tradingPoolChangeLog)) {
                    throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
                }
                //endregion
            }
        } catch (Exception e) {
            log.error("调整交易池库存异常", e);
            throw new GlobalRunTimeException(e.getMessage());
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean confAdjustAmount(TradingPoolConfAdjustAmountDTO request) {
        Assert.isTrue(request.getAmount().compareTo(BigDecimal.ZERO) > 0, () -> new GlobalRunTimeException("金额必须大于0"));
        RLock lock = redisson.getLock(RedisKeyEnum.TRADING_POOL_STOCK.getKey() + request.getDaoId());
        try {
            //尝试数据加锁
            if (!lock.tryLock(30, TimeUnit.SECONDS)) {
                throw new GlobalRunTimeException("操作频繁，请稍后重试");
            }
            TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(request.getId());
            Assert.notNull(tradingPoolConfig, () -> new GlobalRunTimeException("交易池不存在"));
            Assert.isTrue(Objects.equals(tradingPoolConfig.getDaoId(), request.getDaoId()), () -> new GlobalRunTimeException("交易池与DAO不匹配"));
            if (tradingPoolConfig.getStatus()) {
                DateTime localityTime = DateTime.of(LocalTime.now().truncatedTo(ChronoUnit.SECONDS).toString(), "HH:mm:ss");
                DateTime startTime = DateTime.of(tradingPoolConfig.getStartTime());
                DateTime endTime = DateTime.of(tradingPoolConfig.getEndTime());
                if (localityTime.isAfter(startTime) && localityTime.isBefore(endTime)) {
                    throw new GlobalRunTimeException("需要在非活动时间内操作");
                }
            }
            if (request.getIsAdd()) {
                Assert.notNull(request.getAccountType(), () -> new GlobalRunTimeException("账号类型不能为空"));
                List<TradingPoolAccount> tradingPoolAccountList = tradingPoolAccountDao.listByDaoId(tradingPoolConfig.getDaoId());
                Assert.isTrue(CollectionUtil.isNotEmpty(tradingPoolAccountList), () -> new GlobalRunTimeException("冻结账户异常"));
                Assert.isTrue(tradingPoolAccountList.size() == 1, () -> new GlobalRunTimeException("冻结账户异常"));
                TradingPoolAccount tradingPoolAccount = tradingPoolAccountList.get(0);
                if (Objects.equals(request.getAccountType(), TradingPoolAdjustAccountTypeEnum.GIFT_INCOME_ACCOUNT)) {
                    Assert.isTrue(tradingPoolAccount.getGiftIncomeAccount().compareTo(request.getAmount()) >= 0, () -> new GlobalRunTimeException("礼包收入账户金额不足"));
                    Assert.isTrue(tradingPoolAccountDao.reduceGiftIncomeAccount(tradingPoolAccount.getId(), request.getAmount()), () -> new GlobalRunTimeException("礼包收入账户金额不足"));
                    //region 账户变化
                    TradingPoolAccountLog tradingPoolAccountLog = new TradingPoolAccountLog();
                    tradingPoolAccountLog.setDaoId(tradingPoolConfig.getDaoId());
                    tradingPoolAccountLog.setTradingPoolConfigId(tradingPoolConfig.getId());
                    tradingPoolAccountLog.setOperationTime(DateTime.now());
                    tradingPoolAccountLog.setQuantity(request.getAmount());
                    tradingPoolAccountLog.setBeforeChange(tradingPoolAccount.getGiftIncomeAccount());
                    tradingPoolAccountLog.setAfterChange(tradingPoolAccount.getGiftIncomeAccount().subtract(request.getAmount()));
                    tradingPoolAccountLog.setCategory(TradingPoolAccountCategoryEnum.GIFT_INCOME_ACCOUNT);
                    tradingPoolAccountLog.setIsAdd(Boolean.FALSE);
                    tradingPoolAccountLog.setSource(TradingPoolAccountTypeEnum.GIFT_INCOME_ACCOUNT);
                    tradingPoolAccountLog.setBeneficiary(TradingPoolAccountTypeEnum.POOL_ACCOUNT);
                    if (!tradingPoolAccountLogDao.save(tradingPoolAccountLog)) {
                        throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
                    }
                    //endregion
                } else if (Objects.equals(request.getAccountType(), TradingPoolAdjustAccountTypeEnum.FROZEN_ACCOUNT)) {
                    Assert.isTrue(tradingPoolAccount.getFrozenAccount().compareTo(request.getAmount()) >= 0, () -> new GlobalRunTimeException("冻结账户金额不足"));
                    Assert.isTrue(tradingPoolAccountDao.reduceFrozenAccount(tradingPoolAccount.getId(), request.getAmount()), () -> new GlobalRunTimeException("冻结账户金额不足"));
                    //region 账户变化
                    TradingPoolAccountLog tradingPoolAccountLog = new TradingPoolAccountLog();
                    tradingPoolAccountLog.setDaoId(tradingPoolConfig.getDaoId());
                    tradingPoolAccountLog.setTradingPoolConfigId(tradingPoolConfig.getId());
                    tradingPoolAccountLog.setOperationTime(DateTime.now());
                    tradingPoolAccountLog.setQuantity(request.getAmount());
                    tradingPoolAccountLog.setBeforeChange(tradingPoolAccount.getFrozenAccount());
                    tradingPoolAccountLog.setAfterChange(tradingPoolAccount.getFrozenAccount().subtract(request.getAmount()));
                    tradingPoolAccountLog.setCategory(TradingPoolAccountCategoryEnum.FROZEN_ACCOUNT);
                    tradingPoolAccountLog.setIsAdd(Boolean.FALSE);
                    tradingPoolAccountLog.setSource(TradingPoolAccountTypeEnum.FROZEN_ACCOUNT);
                    tradingPoolAccountLog.setBeneficiary(TradingPoolAccountTypeEnum.POOL_ACCOUNT);
                    if (!tradingPoolAccountLogDao.save(tradingPoolAccountLog)) {
                        throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
                    }
                    //endregion
                } else {
                    throw new GlobalRunTimeException("账号类型异常");
                }
                Assert.isTrue(tradingPoolConfigDao.addAmount(tradingPoolConfig.getId(), request.getAmount()), () -> new GlobalRunTimeException("添加交易池金额失败"));
                //region 交易池记录
                TradingPoolChangeLog tradingPoolChangeLog = new TradingPoolChangeLog();
                tradingPoolChangeLog.setTradingPoolConfigId(tradingPoolConfig.getId());
                tradingPoolChangeLog.setRecordTime(DateTime.now());
                tradingPoolChangeLog.setDaoId(tradingPoolAccount.getDaoId());
                tradingPoolChangeLog.setOperationContent(TradingPoolChangeOperationContentEnum.AMOUNT);
                tradingPoolChangeLog.setQuantity(request.getAmount());
                tradingPoolChangeLog.setBeforeChange(tradingPoolConfig.getCurrentAmount());
                tradingPoolChangeLog.setAfterChange(tradingPoolConfig.getCurrentAmount().add(request.getAmount()));
                tradingPoolChangeLog.setType(TradingPoolChangeTypeEnum.ADD);
                tradingPoolChangeLog.setRemark(TradingPoolChangeTypeEnum.ADD.getDescription() + TradingPoolChangeOperationContentEnum.AMOUNT.getDescription());
                if (!tradingPoolChangeLogDao.save(tradingPoolChangeLog)) {
                    throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
                }
                //endregion
            } else {
                List<TradingPoolAccount> tradingPoolAccountList = tradingPoolAccountDao.listByDaoId(tradingPoolConfig.getDaoId());
                Assert.isTrue(CollectionUtil.isNotEmpty(tradingPoolAccountList), () -> new GlobalRunTimeException("冻结账户异常"));
                Assert.isTrue(tradingPoolAccountList.size() == 1, () -> new GlobalRunTimeException("冻结账户异常"));
                TradingPoolAccount tradingPoolAccount = tradingPoolAccountList.get(0);
                Assert.isTrue(tradingPoolAccountDao.addFrozenAccount(tradingPoolAccount.getId(), request.getAmount()), () -> new GlobalRunTimeException("添加冻结账户金额失败"));
                Assert.isTrue(tradingPoolConfig.getCurrentAmount().subtract(request.getAmount()).compareTo(new BigDecimal(100)) >= 0, () -> new GlobalRunTimeException("金额不足，扣除失败"));
                Assert.isTrue(tradingPoolConfigDao.reduceAmount(tradingPoolConfig.getId(), request.getAmount()), () -> new GlobalRunTimeException("金额不足，扣除失败"));
                //region 交易池记录
                TradingPoolChangeLog tradingPoolChangeLog = new TradingPoolChangeLog();
                tradingPoolChangeLog.setTradingPoolConfigId(tradingPoolConfig.getId());
                tradingPoolChangeLog.setRecordTime(DateTime.now());
                tradingPoolChangeLog.setDaoId(tradingPoolAccount.getDaoId());
                tradingPoolChangeLog.setOperationContent(TradingPoolChangeOperationContentEnum.AMOUNT);
                tradingPoolChangeLog.setQuantity(request.getAmount());
                tradingPoolChangeLog.setBeforeChange(tradingPoolConfig.getCurrentAmount());
                tradingPoolChangeLog.setAfterChange(tradingPoolConfig.getCurrentAmount().subtract(request.getAmount()));
                tradingPoolChangeLog.setType(TradingPoolChangeTypeEnum.DEDUCTION);
                tradingPoolChangeLog.setRemark(TradingPoolChangeTypeEnum.DEDUCTION.getDescription() + TradingPoolChangeOperationContentEnum.AMOUNT.getDescription());
                if (!tradingPoolChangeLogDao.save(tradingPoolChangeLog)) {
                    throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
                }
                //endregion
                //region 账户变化
                TradingPoolAccountLog tradingPoolAccountLog = new TradingPoolAccountLog();
                tradingPoolAccountLog.setDaoId(tradingPoolConfig.getDaoId());
                tradingPoolAccountLog.setTradingPoolConfigId(tradingPoolConfig.getId());
                tradingPoolAccountLog.setOperationTime(DateTime.now());
                tradingPoolAccountLog.setQuantity(request.getAmount());
                tradingPoolAccountLog.setBeforeChange(tradingPoolAccount.getFrozenAccount());
                tradingPoolAccountLog.setAfterChange(tradingPoolAccount.getFrozenAccount().add(request.getAmount()));
                tradingPoolAccountLog.setCategory(TradingPoolAccountCategoryEnum.FROZEN_ACCOUNT);
                tradingPoolAccountLog.setIsAdd(Boolean.TRUE);
                tradingPoolAccountLog.setSource(TradingPoolAccountTypeEnum.POOL_ACCOUNT);
                tradingPoolAccountLog.setBeneficiary(TradingPoolAccountTypeEnum.FROZEN_ACCOUNT);
                if (!tradingPoolAccountLogDao.save(tradingPoolAccountLog)) {
                    throw new GlobalRunTimeException(ErrorCode.UPDATE_ERROR);
                }
                //endregion
            }
        } catch (Exception e) {
            log.error("调整交易池金额异常", e);
            throw new GlobalRunTimeException(e.getMessage());
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Boolean.TRUE;
    }

    @Override
    public List<TradingPoolCollectionVO> getCollectionList(TradingPoolCollectionDTO request) {
        List<DigitalCollection> digitalCollectionList = digitalCollectionDao.listByDaoCirculationType(request.getDaoId(), CirculationTypeEnum.TRANS_POOL, request.getCollectionName());
        if (CollectionUtil.isEmpty(digitalCollectionList)) {
            return Collections.emptyList();
        }
        return digitalCollectionList.stream().map(v -> {
            TradingPoolCollectionVO tradingPoolCollectionVO = new TradingPoolCollectionVO();
            tradingPoolCollectionVO.setCollectionId(v.getId());
            tradingPoolCollectionVO.setCollectionName(v.getFirstTitle());
            return tradingPoolCollectionVO;
        }).collect(Collectors.toList());
    }

    @Override
    public List<TradingPoolMarketAccountVO> marketAccount(Long daoId) {
        List<FilterIncomeList> incomeListList = filterIncomeListDao.getByDaoId(daoId);
        if (CollectionUtil.isEmpty(incomeListList)) {
            return Collections.emptyList();
        }
        List<Long> userIdList = incomeListList.stream().map(FilterIncomeList::getUserId).distinct().collect(Collectors.toList());
        List<UserInfo> userInfoList = userInfoDao.getByIdList(userIdList);
        if (CollectionUtil.isEmpty(userInfoList)) {
            return Collections.emptyList();
        }
        return userInfoList.stream().filter(UserInfo::getIsValid).filter(v -> !v.getIsFreeze()).map(v -> {
            TradingPoolMarketAccountVO tradingPoolMarketAccountVO = new TradingPoolMarketAccountVO();
            tradingPoolMarketAccountVO.setUserId(v.getId());
            tradingPoolMarketAccountVO.setUserTel(v.getUserTel());
            tradingPoolMarketAccountVO.setNickName(v.getNickName());
            return tradingPoolMarketAccountVO;
        }).collect(Collectors.toList());
    }

    @Override
    public IPage<TradingPoolChangeLogVO> changeLogPage(TradingPoolChangeLogDTO request) {
        IPage<TradingPoolChangeLog> page = tradingPoolChangeLogDao.page(request.toMp(), request.getConfId(), request.getDaoId(), request.getType());
        return page.convert(v -> {
            TradingPoolChangeLogVO tradingPoolChangeLogVO = new TradingPoolChangeLogVO();
            tradingPoolChangeLogVO.setRecordTime(v.getRecordTime());
            tradingPoolChangeLogVO.setOperationContent(v.getOperationContent());
            tradingPoolChangeLogVO.setOperationContentName(v.getOperationContent().getDescription());
            tradingPoolChangeLogVO.setQuantity(v.getQuantity());
            tradingPoolChangeLogVO.setBeforeChange(v.getBeforeChange());
            tradingPoolChangeLogVO.setAfterChange(v.getAfterChange());
            return tradingPoolChangeLogVO;
        });
    }

    @Override
    public TradingPoolSellDetailsVO tradingPoolSellDetails(Long id, UserInfo userInfo) {
        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(id);
        if (BeanUtil.isEmpty(tradingPoolConfig)) {
            throw new GlobalRunTimeException("当前交易池已关闭");
        }
        // 获取用户当前对应的有效交易藏品有多少个.
        Integer count = userCollectionDao.getCountByCollectionIdAndLtNowDate(tradingPoolConfig.getCollectionId(), userInfo.getId());
        TradingPoolSellDetailsVO tradingPoolSellDetailsVO = new TradingPoolSellDetailsVO(tradingPoolConfig);
        tradingPoolSellDetailsVO.setFeeRate(tradingPoolConfig.getFeeRate());
        tradingPoolSellDetailsVO.setGas(tradingPoolConfig.getGas());
        tradingPoolSellDetailsVO.setHoldSum(count != null ? count : 0);
        tradingPoolSellDetailsVO.setStartTime(DateTime.of(tradingPoolConfig.getStartTime()));
        tradingPoolSellDetailsVO.setEndTime(DateTime.of(tradingPoolConfig.getEndTime()));
        tradingPoolSellDetailsVO.setCurrentPrice(tradingPoolConfig.getCurrentPrice());
        tradingPoolSellDetailsVO.setMaxDecreasePercentage(tradingPoolConfig.getMaxDecreasePercentage());
        tradingPoolSellDetailsVO.setMaxIncreasePercentage(tradingPoolConfig.getMaxIncreasePercentage());
        tradingPoolSellDetailsVO.setHandlingCharge(tradingPoolConfig.getCurrentPrice().multiply(tradingPoolConfig.getFeeRate()).setScale(2, BigDecimal.ROUND_DOWN));
        return tradingPoolSellDetailsVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean confShareProfitPanel(TradingPoolConfShareProfitPanelDTO request) {
        Boolean flag = tradingPoolConfigDao.updateShareProfitPanel(request.getShareProfitPanelId(), request.getId());
        if (!flag) {
            throw new GlobalRunTimeException("分润模版配置异常!");
        }
        return Boolean.TRUE;
    }

    @Override
    public TradingPoolSharePanelVO getShareProfitPanel(Long id) {
        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(id);
        Assert.notNull(tradingPoolConfig, () -> new GlobalRunTimeException("配置不存在"));
        if (Objects.isNull(tradingPoolConfig.getShareProfitPanelId())) {
            return null;
        }
        ShareProfitPanel shareProfitPanel = shareProfitPanelDao.getById(tradingPoolConfig.getShareProfitPanelId());
        if (Objects.isNull(shareProfitPanel)) {
            return null;
        }
        TradingPoolSharePanelVO tradingPoolSharePanelVO = new TradingPoolSharePanelVO();
        tradingPoolSharePanelVO.setShareProfitPanelName(shareProfitPanel.getPanelName());
        tradingPoolSharePanelVO.setShareProfitPanelId(shareProfitPanel.getId());
        return tradingPoolSharePanelVO;
    }

    @Override
    public TradingPoolQueryUsableVO queryUsable(TradingPoolQueryUsableDTO request) {
        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(request.getId());
        Assert.notNull(tradingPoolConfig, () -> new GlobalRunTimeException("交易池不存在"));
        BigDecimal usable = BigDecimal.ZERO;
        if (Objects.equals(request.getOperation(), TradingPoolQueryOperationEnum.ADD)) {
            if (Objects.equals(request.getContent(), TradingPoolQueryContentEnum.NFT)) {
                DigitalCollection collection = digitalCollectionDao.getById(tradingPoolConfig.getCollectionId());
                usable = Objects.nonNull(collection) ? new BigDecimal(collection.getUsableCount()) : BigDecimal.ZERO;
            } else if (Objects.equals(request.getContent(), TradingPoolQueryContentEnum.AMOUNT)) {
                Assert.notNull(request.getAccountType(), () -> new GlobalRunTimeException("账户不能为空"));
                List<TradingPoolAccount> tradingPoolAccountList = tradingPoolAccountDao.listByDaoId(tradingPoolConfig.getDaoId());
                if (CollectionUtil.isNotEmpty(tradingPoolAccountList) && tradingPoolAccountList.size() == 1) {
                    TradingPoolAccount tradingPoolAccount = tradingPoolAccountList.get(0);
                    if (Objects.equals(request.getAccountType(), TradingPoolQueryAccountEnum.CORPORATE_ACCOUNT)) {
                        usable = tradingPoolAccount.getGiftIncomeAccount();
                    } else if (Objects.equals(request.getAccountType(), TradingPoolQueryAccountEnum.FROZEN_ACCOUNT)) {
                        usable = tradingPoolAccount.getFrozenAccount();
                    }
                }
            }
        } else if (Objects.equals(request.getOperation(), TradingPoolQueryOperationEnum.DEDUCTION)) {
            if (Objects.equals(request.getContent(), TradingPoolQueryContentEnum.NFT)) {
                usable = new BigDecimal(tradingPoolConfig.getCurrentStock());
            } else if (Objects.equals(request.getContent(), TradingPoolQueryContentEnum.AMOUNT)) {
                usable = tradingPoolConfig.getCurrentAmount();
            }
        }
        TradingPoolQueryUsableVO tradingPoolQueryUsableVO = new TradingPoolQueryUsableVO();
        tradingPoolQueryUsableVO.setUsable(usable);
        return tradingPoolQueryUsableVO;
    }

    @Override
    public IPage<TradingPoolAccountRecordVO> accountRecordPage(TradingPoolAccountRecordDTO request) {
        IPage<TradingPoolAccountLog> page = tradingPoolAccountLogDao.recordPage(request.toMp(), request.getDaoId(), request.getCategory(), request.getSource(), request.getStartTime(), request.getEndTime());
        return page.convert(v -> {
            TradingPoolAccountRecordVO tradingPoolAccountRecordVO = new TradingPoolAccountRecordVO();
            tradingPoolAccountRecordVO.setOperationTime(v.getOperationTime());
            tradingPoolAccountRecordVO.setSource(v.getSource());
            tradingPoolAccountRecordVO.setSourceName(v.getSource().getDescription());
            tradingPoolAccountRecordVO.setBeneficiary(v.getBeneficiary());
            tradingPoolAccountRecordVO.setBeneficiaryName(v.getBeneficiary().getDescription());
            tradingPoolAccountRecordVO.setBeforeChange(v.getBeforeChange());
            tradingPoolAccountRecordVO.setAfterChange(v.getAfterChange());
            tradingPoolAccountRecordVO.setQuantity(v.getQuantity());
            tradingPoolAccountRecordVO.setIsAdd(v.getIsAdd());
            tradingPoolAccountRecordVO.setRemark(StrUtil.isNotBlank(v.getRemark()) ? v.getRemark() : "");
            tradingPoolAccountRecordVO.setAccount("");
            return tradingPoolAccountRecordVO;
        });
    }

    @Override
    public TradingPoolAccountInfoVO accountInfo(TradingPoolAccountInfoDTO request) {
        BigDecimal amount = BigDecimal.ZERO;
        List<TradingPoolAccount> tradingPoolAccountList = tradingPoolAccountDao.listByDaoId(request.getDaoId());
        if (CollectionUtil.isNotEmpty(tradingPoolAccountList)) {
            Assert.isTrue(tradingPoolAccountList.size() == 1, () -> new GlobalRunTimeException("账户异常"));
            TradingPoolAccount tradingPoolAccount = tradingPoolAccountList.get(0);
            amount = request.getAccountType().equals(TradingPoolAccountCategoryEnum.GIFT_INCOME_ACCOUNT )? tradingPoolAccount.getGiftIncomeAccount() : tradingPoolAccount.getFrozenAccount();
        }
        TradingPoolAccountInfoVO tradingPoolAccountInfoVO = new TradingPoolAccountInfoVO();
        tradingPoolAccountInfoVO.setAmount(amount);
        return tradingPoolAccountInfoVO;
    }

    @Override
    public List<TradingPoolConfig> getActivatedTradingPoolConfigList() {
        return tradingPoolConfigDao.getActivatedTradingPoolConfigList();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void destroyTradingPool(TradingPoolConfig tradingPoolConfig) {
        // 销毁比例
        BigDecimal destructionPercentage = tradingPoolConfig.getDestructionPercentage();
        // 销毁的NFT直接没有 钱分给其他账号

        // 销毁要分的钱
        BigDecimal destroyAmount = destructionPercentage.multiply(tradingPoolConfig.getCurrentAmount());
        if (NumberUtil.isLessOrEqual(destroyAmount, BigDecimal.ZERO)) {
            throw new GlobalRunTimeException(StrUtil.format("销毁金额额度为:{},无法销毁", destroyAmount));
        }
        // 销毁的NFT数量
        Integer destroyNft = Convert.toInt(destructionPercentage.multiply(Convert.toBigDecimal(tradingPoolConfig.getCurrentStock()).setScale(0, BigDecimal.ROUND_DOWN)));
        if (destroyNft <= 0) {
            throw new GlobalRunTimeException("销毁的nft数量小于等于0  无法销毁");
        }
        // nft销毁直接扣掉库存,先扣交易池库存 在扣藏品库存  记录日志
        Boolean subInventory = tradingPoolConfigDao.subInventory(tradingPoolConfig.getId(), destroyNft);
        if (!subInventory) {
            log.info("交易池库存扣减失败,交易池ID:{}", tradingPoolConfig.getId());
            throw new GlobalRunTimeException("交易池库存扣减失败");
        }
        // 扣掉藏品总库存和可用库存
        Boolean subSumCount = digitalCollectionDao.subSumCount(tradingPoolConfig.getCollectionId(), destroyNft);
        Integer subUsableStock = digitalCollectionDao.subUsableStock(tradingPoolConfig.getCollectionId(), destroyNft);
        if (!subSumCount && subUsableStock <= 0) {
            log.info("藏品库存扣减失败,藏品ID:{}", tradingPoolConfig.getCollectionId());
            throw new GlobalRunTimeException("藏品库存扣减失败");
        }
        // 扣钱
        Boolean subAmount = tradingPoolConfigDao.subAmount(tradingPoolConfig.getId(), destroyAmount);
        if (!subAmount) {
            log.info("交易池金额扣减失败,交易池ID:{}", tradingPoolConfig.getId());
            throw new GlobalRunTimeException("交易池金额扣减失败");
        }

        List<CollectionChainInitDetailsLog> destroyDetailsData = getCollectionChainInitDetailsLogs(tradingPoolConfig, destroyNft);

        UserChainInfo userChainInfo = userChainInfoDao.getByUserId(1L);
        // 插入销毁的日志
        List<TradingPoolDestroyRecord> tradingPoolDestroyRecords = new ArrayList<>();
        for (CollectionChainInitDetailsLog destroyDetailsDatum : destroyDetailsData) {
            UserCollectionChainLog userCollectionChainLog = new UserCollectionChainLog();
            userCollectionChainLog.setUserId(1L);
            userCollectionChainLog.setDaoId(tradingPoolConfig.getDaoId());
            userCollectionChainLog.setUserCollectionId(0L);
            userCollectionChainLog.setCollectionType(ReceivedCollectionType.TRADING_DESTROY);
            userCollectionChainLog.setType(UserChainTypeEnum.TRADING_DESTROY);
            userCollectionChainLog.setStatus(UserChainLogStatusEnum.WAIT);
            userCollectionChainLog.setCollectionId(tradingPoolConfig.getCollectionId());
            userCollectionChainLog.setContractAddress(destroyDetailsDatum.getContractAddress());
            userCollectionChainLog.setDeployTransactionHash(destroyDetailsDatum.getTransactionHash());
            userCollectionChainLog.setProductId("0");
            userCollectionChainLog.setAddress(userChainInfo.getAddress());
            userCollectionChainLog.setTokenId(destroyDetailsDatum.getTokenId());
            userCollectionChainLog.setFromUserId(1L);
            userCollectionChainLog.setFromUserCollectionId(0L);
            userCollectionChainLog.setFromUserAddress(userChainInfo.getAddress());
            userCollectionChainLog.setToUserId(0L);
            userCollectionChainLog.setToUserCollectionId(0L);
            userCollectionChainLog.setToUserAddress("");
            userCollectionChainLog.setCollectionChainId(0L);
            userCollectionChainLog.insert();

            //新增销毁记录数据
            TradingPoolDestroyRecord tradingPoolDestroyRecord = new TradingPoolDestroyRecord();
            tradingPoolDestroyRecord.setUserCollectionChainLogId(userCollectionChainLog.getId());
            tradingPoolDestroyRecord.setCollectionId(destroyDetailsDatum.getCollectionId());
            tradingPoolDestroyRecord.setStatus(destroyDetailsDatum.getStatus());
            tradingPoolDestroyRecord.setDealStatus(destroyDetailsDatum.getDealStatus());
            tradingPoolDestroyRecord.setContractAddress(destroyDetailsDatum.getContractAddress());
            tradingPoolDestroyRecord.setDeployTransactionHash(destroyDetailsDatum.getDeployTransactionHash());
            tradingPoolDestroyRecord.setTransactionHash(destroyDetailsDatum.getTransactionHash());
            tradingPoolDestroyRecord.setUserAddress(destroyDetailsDatum.getUserAddress());
            tradingPoolDestroyRecord.setNumbering(destroyDetailsDatum.getNumbering());
            tradingPoolDestroyRecord.setTokenId(destroyDetailsDatum.getTokenId());
            tradingPoolDestroyRecord.setCollectionUuid(destroyDetailsDatum.getCollectionUuid());
            tradingPoolDestroyRecords.add(tradingPoolDestroyRecord);
        }

        List<Long> ids = destroyDetailsData.stream().map(CollectionChainInitDetailsLog::getId).collect(Collectors.toList());
        boolean destroyDetailsDataUpdate = collectionChainInitDetailsLogDao.updateDealStatusByIds(CollectionChainDealStatusEnum.TRADED, ids);
        if (!destroyDetailsDataUpdate) {
            throw new GlobalRunTimeException("交易池销毁collectionChainInitDetailsLog更新失败");
        }

        int tradingPoolDestroyRecordSize = tradingPoolDestroyRecordDao.saveBatchRecord(tradingPoolDestroyRecords);
        if (tradingPoolDestroyRecordSize != tradingPoolDestroyRecords.size()) {
            throw new GlobalRunTimeException("交易池销毁记录保存失败");
        }

        // 全都成功了,记录扣钱的日志.日志分两个表,一个是详情 一个是汇总
        TradingPoolChangeLog nftDestroyLog = new TradingPoolChangeLog();
        nftDestroyLog.setTradingPoolConfigId(tradingPoolConfig.getId());
        nftDestroyLog.setRecordTime(DateUtil.date());
        nftDestroyLog.setDaoId(tradingPoolConfig.getDaoId());
        nftDestroyLog.setOperationContent(TradingPoolChangeOperationContentEnum.NFT);
        nftDestroyLog.setQuantity(Convert.toBigDecimal(destroyNft));
        nftDestroyLog.setBeforeChange(Convert.toBigDecimal(tradingPoolConfig.getCurrentStock()));
        nftDestroyLog.setAfterChange(Convert.toBigDecimal(tradingPoolConfig.getCurrentStock()).subtract(Convert.toBigDecimal(destroyNft)));
        nftDestroyLog.setRemark("交易池销毁NFT");
        nftDestroyLog.setType(TradingPoolChangeTypeEnum.DESTRUCTION);
        tradingPoolChangeLogDao.save(nftDestroyLog);


        TradingPoolChangeLog destroyAmountLog = new TradingPoolChangeLog();
        destroyAmountLog.setTradingPoolConfigId(tradingPoolConfig.getId());
        destroyAmountLog.setRecordTime(DateUtil.date());
        destroyAmountLog.setDaoId(tradingPoolConfig.getDaoId());
        destroyAmountLog.setOperationContent(TradingPoolChangeOperationContentEnum.AMOUNT);
        destroyAmountLog.setQuantity(destroyAmount);
        destroyAmountLog.setBeforeChange(tradingPoolConfig.getCurrentAmount());
        destroyAmountLog.setAfterChange(tradingPoolConfig.getCurrentAmount().subtract(destroyAmount));
        destroyAmountLog.setRemark("交易池销毁金额");
        destroyAmountLog.setType(TradingPoolChangeTypeEnum.DESTRUCTION);
        tradingPoolChangeLogDao.save(destroyAmountLog);

        List<TradingDestructionConfig> tradingDestructionConfigs = tradingDestructionConfigDao.listByConfId(tradingPoolConfig.getId());
        for (TradingDestructionConfig tradingDestructionConfig : tradingDestructionConfigs) {

            switch (tradingDestructionConfig.getAccountType()) {
                // 礼包收入账户
                case CORPORATE_ACCOUNT:
                    // 获取礼包收入账户钱
                    List<TradingPoolAccount> tradingPoolAccounts = tradingPoolAccountDao.listByDaoId(tradingPoolConfig.getDaoId());
                    if (tradingPoolAccounts.size() == 1) {
                        if (!tradingPoolAccountDao.depositIntoTheGiftPackageAccount(tradingPoolConfig.getDaoId(), destroyAmount.multiply(tradingDestructionConfig.getTransferPercentage()))) {
                            throw new GlobalRunTimeException("礼包收入账户入金失败,账户daoId:{}" + tradingPoolConfig.getDaoId());
                        }
                        // 用户的账户数据
                        TradingPoolAccount tradingPoolAccount = tradingPoolAccounts.get(0);

                        TradingPoolAccountLog tradingPoolAccountLog = new TradingPoolAccountLog();
                        tradingPoolAccountLog.setDaoId(tradingPoolConfig.getDaoId());
                        tradingPoolAccountLog.setTradingPoolConfigId(tradingPoolConfig.getId());
                        tradingPoolAccountLog.setOperationTime(DateUtil.date());
                        tradingPoolAccountLog.setQuantity(destroyAmount.multiply(tradingDestructionConfig.getTransferPercentage()));
                        tradingPoolAccountLog.setBeforeChange(tradingPoolAccount.getGiftIncomeAccount());
                        tradingPoolAccountLog.setAfterChange(tradingPoolAccount.getGiftIncomeAccount().add(destroyAmount.multiply(tradingDestructionConfig.getTransferPercentage())));
                        tradingPoolAccountLog.setCategory(TradingPoolAccountCategoryEnum.GIFT_INCOME_ACCOUNT);
                        tradingPoolAccountLog.setIsAdd(Boolean.TRUE);
                        tradingPoolAccountLog.setSource(TradingPoolAccountTypeEnum.POOL_ACCOUNT);
                        tradingPoolAccountLog.setBeneficiary(TradingPoolAccountTypeEnum.GIFT_INCOME_ACCOUNT);
                        tradingPoolAccountLog.setRemark("销毁入账");
                        tradingPoolAccountLogDao.save(tradingPoolAccountLog);
                    } else {
                        throw new GlobalRunTimeException("账号配置异常,无法进行下一步操作");
                    }
                    break;
                // 市值账户
                case MARKET_ACCOUNT:
                    // 市值账户id.给这个账户加钱
                    Long userId = tradingDestructionConfig.getUserId();
                    userWalletService.addIncome(userId, "", destroyAmount.multiply(tradingDestructionConfig.getTransferPercentage()), BigDecimal.ZERO, WalletDetailTypeEnum.TRANSACTION_POOL_DESTRUCTION, ExpenditureRecordTypeEnum.BACKSTAGE_ADD);
                    break;
                default:
                    break;
            }
        }
    }


    private List<CollectionChainInitDetailsLog> getCollectionChainInitDetailsLogs(TradingPoolConfig tradingPoolConfig, Integer destroyNft) {
        List<CollectionChainInitDetailsLog> destroyDetailsData;
        RLock lock = redisson.getLock(RedisKeyEnum.GET_COLLECTION_TRANSFER.getKey() + tradingPoolConfig.getCollectionId().toString());
        try {
            boolean res = lock.tryLock(5, TimeUnit.SECONDS);
            if (!res) {
                throw new GlobalRunTimeException("交易池藏品销毁 GET_COLLECTION_TRANSFER 锁获取失败");
            }


            // 获取缓存中需要排除的编号数据
            String numberingKey = RedisKeyEnum.QUEUE_COLLECTION_ID.getKey() + tradingPoolConfig.getCollectionId().toString();
            List<Long> list = redisTemplate.opsForList().range(numberingKey, 0, -1);
            destroyDetailsData = collectionChainInitDetailsLogDao.getExcludeListByCollectionId(tradingPoolConfig.getCollectionId(), list, destroyNft);

            if (destroyDetailsData.size() < destroyNft && CollectionUtils.isNotEmpty(list)) {
                while (true) {
                    if (destroyDetailsData.size() == destroyNft) {
                        break;
                    }
                    Object id = redisTemplate.opsForList().rightPop(RedisKeyEnum.QUEUE_COLLECTION_ID.getKey() + tradingPoolConfig.getCollectionId());
                    if (ObjectUtil.isNull(id)) {
                        break;
                    }
                    CollectionChainInitDetailsLog ccs = collectionChainInitDetailsLogDao.getById(Long.parseLong(id.toString()));
                    if (ccs.getStatus() != UserChainLogStatusEnum.SUCCESS || ccs.getDealStatus() != CollectionChainDealStatusEnum.TRADEABLE) {
                        continue;
                    }
                    destroyDetailsData.add(ccs);
                }
            }

            if (destroyDetailsData.size() != destroyNft) {
                throw new GlobalRunTimeException("交易池藏品销毁失败 请检查藏品编号");
            }
        } catch (InterruptedException e) {
            throw new GlobalRunTimeException("交易池藏品销毁redis中的id失败");
        } finally {
            lock.unlock();
        }
        return destroyDetailsData;
    }

    @Override
    public void initTradingPool() {
        tradingPoolConfigDao.initTradingPool();
    }

    @Override
    public CurrentPriceVO whetherItCanBePaid(WhetherItCanBePaidDTO dto) {
        // 当前的售货柜根据埋点等一系列操作是否可以进行支付.
        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(dto.getId());
        BigDecimal payAmount = tradingPoolConfig.getCurrentPrice();
        // 用户可接受的最大价格
        // 最低价
        BigDecimal maxDecreasePrice = (BigDecimal.ONE.subtract(dto.getMaxDecreasePercentage())).multiply(dto.getPresentPrice());
        // 最高价
        BigDecimal maxIncreasePrice = (dto.getMaxIncreasePercentage().add(BigDecimal.ONE)).multiply(dto.getPresentPrice());
        if (NumberUtil.isLess(payAmount, maxDecreasePrice) || NumberUtil.isGreater(payAmount, maxIncreasePrice)) {
            throw new GlobalRunTimeException("当前价格不在市场价格区间");
        }
        // 判断当前价格是否在规定的范围内
        if (NumberUtil.isLess(payAmount, tradingPoolConfig.getMinPrice()) || NumberUtil.isGreater(payAmount, tradingPoolConfig.getMaxPrice())) {
            throw new GlobalRunTimeException("当前价格不在交易范围内");
        }
        CurrentPriceVO currentPriceVO = new CurrentPriceVO();
        currentPriceVO.setCurrentPrice(tradingPoolConfig.getCurrentPrice());
        return currentPriceVO;
    }

    /**
     * 校验创建交易池请求参数
     *
     * @param request
     */
    private void validateCreateConf(TradingPoolCreateConfRpcDTO request) {
        String name = request.getName().trim();
        Assert.notBlank(name, () -> new GlobalRunTimeException("交易池名称不能为空"));
        Assert.isTrue(name.length() <= 6, () -> new GlobalRunTimeException("交易池名称限制6个字符"));
        Assert.isTrue(request.getEndTime().after(request.getStartTime()), () -> new GlobalRunTimeException("结束时间必须晚于开始时间"));
        Assert.isTrue(request.getMaxPrice().compareTo(request.getMinPrice()) >= 0, () -> new GlobalRunTimeException("最大价格不能小于最小价格"));
        Assert.isTrue(CollectionUtil.isEmpty(tradingPoolConfigDao.listByCollectionId(request.getCollectionId())), () -> new GlobalRunTimeException("藏品已配置交易池"));
    }

    /**
     * 校验修改交易池请求参数
     *
     * @param request
     */
    private void validateUpdateConf(TradingPoolUpdateConfDTO request) {
        String name = request.getName().trim();
        Assert.notBlank(name, () -> new GlobalRunTimeException("交易池名称不能为空"));
        Assert.isTrue(name.length() <= 6, () -> new GlobalRunTimeException("交易池名称限制6个字符"));
        Assert.isTrue(request.getEndTime().after(request.getStartTime()), () -> new GlobalRunTimeException("结束时间必须晚于开始时间"));
        Assert.isTrue(request.getMaxPrice().compareTo(request.getMinPrice()) >= 0, () -> new GlobalRunTimeException("最大价格不能小于最小价格"));
    }

}




